package io.github.wslxm.springbootplus2.core.utils;


import cn.hutool.core.codec.Base64;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.util.ResourceUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Map;

/**
 * 导入导出 文件工具
 *
 * @author wangsong
 * @version 1.0.0
 * @email 1720696548@qq.com
 * @date 2022/12/22 0022 21:16
 */
@Slf4j
public class XjFileUtil {


    /**
     * 创建文件夹
     *
     * @param path
     * @return void
     * @date 2019/11/22 15:18
     */
    public static void mkdirFile(String path) {
        // 不存在创建文件夹
        File file = new File(path);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    /**
     * 创建文件
     *
     * @param path 全路径 指向文件
     * @return
     */
    public static boolean makeFile(String path) {
        File file = new File(path);
        if (file.exists()) {
            log.info("文件已存在！");
            return false;
        }
        if (path.endsWith(File.separator)) {
            log.info("不能为目录！");
            return false;
        }
        if (!file.getParentFile().exists()) {
            if (!file.getParentFile().mkdirs()) {
                log.info("创建目标文件所在目录失败！");
                return false;
            }
        }
        try {
            if (file.createNewFile()) {
                log.info("创建文件" + path + "成功！");
                return true;
            } else {
                log.info("创建文件" + path + "失败！");
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            log.info("创建文件" + path + "失败！" + e.getMessage());
            return false;
        }
    }


    /**
     * 文件上传 (MultipartFile)
     *
     * @param multipartFile 上传文件
     * @param path          保存地址
     * @return 本地文件
     */
    public static void uploadFile(MultipartFile multipartFile, String path) {
        // 创建放文件的目录
        try {
            uploadFile(multipartFile.getInputStream(), path, multipartFile.getOriginalFilename());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 文件上传 (inputStream)
     *
     * @param inputStream 上传文件
     * @param path        保存地址
     * @param filename    保存后的文件名
     * @return 本地文件
     */
    public static void uploadFile(InputStream inputStream, String path, String filename) {
        // 创建放文件的目录
        Path directory = Paths.get(path);
        try {
            // 判断目录是否存在，不存在创建
            if (!Files.exists(directory)) {
                Files.createDirectories(directory);
            }
            // 拷贝文件
            Files.copy(inputStream, directory.resolve(filename), StandardCopyOption.REPLACE_EXISTING);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 文件获取  (使用可访问的 url 获取)
     * <p>
     * 通常作用于文件读取,如 图片/pdf/word等，注意：该方法未关闭流，需要主动关闭
     * 如需 BufferedReader, 自行使用 new BufferedReader(new InputStreamReader(cin, StandardCharsets.UTF_8))
     * </P>
     *
     * @param url 可访问的url
     * @return void
     * @author wangsong
     * @date 2020/8/26 0026 17:00
     * @version 1.0.1
     */
    public static InputStream getInputStreamByUrl(String url) {
        String fileName = url.substring(url.lastIndexOf("/") + 1);
        int index = url.lastIndexOf("/");
        String newFilePath = url.substring(0, index + 1);
        InputStream in = null;
        try {
            // 编码
            fileName = URLEncoder.encode(fileName.trim(), "utf-8");
            // 处理空格
            fileName = fileName.replaceAll("\\+", "%20");
            URL newUrl = new URL(newFilePath + fileName);
            URLConnection conn = newUrl.openConnection();
            in = conn.getInputStream();
        } catch (IOException e) {
            log.error("错误： 尝试使用原连接获取");
            try {
                // 编码
                URL newUrl = new URL(url);
                URLConnection conn = newUrl.openConnection();
                in = conn.getInputStream();
                log.error("使用原连接获取成功");
            } catch (IOException e1) {
                log.error(e1.toString());
                log.error("使用原连接获取失败");
            }
        }
        return in;
    }


    /**
     * 关闭流
     *
     * @author wangsong
     * @email 1720696548@qq.com
     * @date 2023/1/18 0018 11:47
     * @version 1.0.0
     */
    public static void close(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }


    /**
     * 文件获取  (获取 resources 下的文件, 支持打 jar 后在服务器中获取)
     * 如需 BufferedReader, 自行使用  new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
     *
     * @author wangsong
     * @email 1720696548@qq.com
     * @date 2022/12/22 0022 21:06
     * @version 1.0.0
     */
    public static InputStream getInputStreamByClasspath(String path) {
        Resource resource = new ClassPathResource(path);
        try {
            return resource.getInputStream();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取 classpath 下指定的文件 (不能获取文件夹)
     * <P>
     *     从 resources 下的跟目录开始获取 (如: resources/static 获取定义为 static/xxxx)
     * </P>
     *
     * @author wangsong
     * @email 1720696548@qq.com
     * @date 2022/12/22 0022 21:06
     * @version 1.0.0
     */
    public static FileInputStream getFileInputStreamByClasspath(String path) {
        // 读取目录文件打压缩包
        try {
            return new FileInputStream(ResourceUtils.getURL("classpath:").getPath() + "/" + path);
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 文件生成 (根据 Template 模版)
     * <P>
     *   使用  Classpath 下的模板替换参数后生成新的文件
     * </P>
     *
     * @param templatesPath 模板路径
     * @param generatePath 生成路径
     * @param params 替换参数
     * @return boolean
     * @author wangsong
     * @date 2022/11/3 0003 12:06
     * @version 1.0.0
     */
    public static boolean generateFile(String templatesPath, String generatePath, Map<String, String> params) {
        try {
            // 获取代码模版文件
            BufferedReader br = new BufferedReader(new InputStreamReader(getInputStreamByClasspath(templatesPath), StandardCharsets.UTF_8));
            // 输出
            BufferedWriter bw = new BufferedWriter(new FileWriter(generatePath));
            String line = null;
            while ((line = br.readLine()) != null) {
                String newLine = line;
                for (String key : params.keySet()) {
                    newLine = newLine.replace(key, params.get(key));
                }
                bw.write(newLine);
                bw.newLine();
                bw.flush();
            }
            br.close();
            bw.close();
        } catch (Exception e) {
            log.error(e.toString());
        }
        return true;
    }


    /**
     * 文件拷贝 (将 classpath 中的文件拷贝到其他目录)
     *
     * @param path    不能以/开头   指向文件不能是目录
     * @param newPath 指向文件不能是目录
     */
    public static void copyFileByClasspath(String path, String newPath) {
        InputStream in = getInputStreamByClasspath(path);
        try {
            //创建新文件
            makeFile(newPath);
            //将流写入新文件
            write(in, newPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 输入流写入文件
     *
     * @param in       输入流
     * @param filePath 文件保存目录路径
     * @throws IOException
     */
    private static void write(InputStream in, String filePath) throws IOException {
        OutputStream os = Files.newOutputStream(Paths.get(filePath));
        int len = 8192;
        byte[] buffer = new byte[len];
        while ((len = in.read(buffer, 0, len)) != -1) {
            os.write(buffer, 0, len);
        }
        os.close();
        in.close();
    }


    /**
     * InputStream 转  file (getClasspathFile 获取的文件流可以通过这个方法保存到指定位置中)
     * 只支持单个文件
     *
     * @param in 文件流
     * @param mkdir 文件路径
     * @param fileName 文件名称
     */
    public static File inputStreamToFile(InputStream in, String mkdir, String fileName) {
        File file = new File(mkdir);
        // 创建放文件的目录
        if (!file.exists()) {
            file.mkdirs();
        }
        OutputStream os = null;
        try {
            file = new File(mkdir + "/" + fileName);
            // 创建放文件的目录
            if (!file.exists()) {
                file.createNewFile();
            }
            os = new FileOutputStream(file);
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = in.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
            os.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                os.close();
                in.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return file;
    }


    /**
     * file 转 base64
     *
     * @param file
     * @return java.lang.String
     * @author wangsong
     * @date 2021/3/3 0003 15:00
     * @version 1.0.1
     */
    public static String fileToBase64(File file) {
        if (file == null) {
            return null;
        }
        String base64 = null;
        try (FileInputStream is = new FileInputStream(file)) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int len = 0;
            byte[] b = new byte[1024];
            while ((len = is.read(b, 0, b.length)) != -1) {
                baos.write(b, 0, len);
            }
            byte[] buffer = baos.toByteArray();
            base64 = Base64.encode(buffer);
        } catch (IOException e) {
            log.error(e.toString());
        }
        return base64;
    }


    /**
     * base 64 转 file
     *
     * @param base64
     * @return java.io.File
     * @author wangsong
     * @date 2021/3/3 0003 15:00
     * @version 1.0.1
     */
    public static File base64ToFile(String base64) {
        if (base64 == null || "" .equals(base64)) {
            return null;
        }
        byte[] buff = Base64.decode(base64);
        File file = null;
        try {
            file = File.createTempFile("tmp", null);
        } catch (IOException e) {
            log.error(e.toString());
        }
        try (FileOutputStream fout = new FileOutputStream(file)) {
            fout.write(buff);
        } catch (IOException e) {
            log.error(e.toString());
        }
        return file;
    }
}
